﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace net.entity.framework.common {
    public static class ReflectUtil {
        public static readonly Object voidObj = new object();
        public static readonly Object[] voidObj1 = new object[1];
        private const BindingFlags Flags = BindingFlags.Public | BindingFlags.Instance;

        /// <summary>
        /// Gets the reflected value of property in object
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <param name="strict"></param>
        /// <returns></returns>
        public static T GetPropertyValue<T>(this object obj, string propertyName, bool strict) {
            var pi = obj.GetType().GetPropertyInfo(propertyName, strict);
            return pi.GetValue<T>(obj, strict);
        }

        public static T GetPropertyValue<T>(this object obj, PropertyInfo pi, bool strict) {
            return pi.GetValue<T>(obj, strict);
        }

        public static PropertyInfo GetPropertyInfo(this Type type, string propertyName, BindingFlags bindingFlags,
                                                   bool strict) {
            var pi = type.GetProperty(propertyName, bindingFlags);
            if (pi != null) {
                return pi;
            }
            if (strict) {
                throw new Exception("Reflect Util");
            }
            return null;
        }

        public static PropertyInfo GetPropertyInfo(this Type type, string propertyName, bool strict) {
            var pi = type.GetProperty(propertyName, Flags);
            if (pi != null) {
                return pi;
            }
            if (strict) {
                throw new Exception("Reflect Util");
            }
            return null;
        }


        public static PropertyInfo[] GetPropertyInfos(this Type type, BindingFlags bindingFlags) {
            return type.GetProperties(bindingFlags);
        }

        public static T GetValue<T>(this PropertyInfo pi, object obj, object[] index, bool strict) {
            var value = default(T);
            try {
                value = (T) pi.GetValue(obj, index);
            } catch (Exception) {
                if (strict) {
                    throw;
                }
            }
            return value;
        }

        public static T GetValue<T>(this PropertyInfo pi, object obj, bool strict) {
            return pi.GetValue<T>(obj, null, strict);
        }

        public static T GetValue<T>(this PropertyInfo pi, bool strict) {
            return pi.GetValue<T>(voidObj, strict);
        }

        public static ConstructorInfo GetConstructorInfo(this Type type, IEnumerable<object> args, BindingFlags flags, bool strict)
        {
            var types = new List<Type>();
            args.All(x =>
                     {
                         types.Add(x.GetType());
                         return true;
                     });
            var constructor = type.GetConstructor(flags, null, CallingConventions.Any, types.ToArray(), null);

            if (constructor == null)
            {
                if (strict)
                {
                    var argTypes = String.Join(", ", types.Select(x => x.Name).ToArray());
                    throw new Exception(string.Format("Cannot find constructor '{0}({1})' with arguments",
                                                      type.Name,
                                                      argTypes));
                }
            }
            return constructor;
        }
    }
}